// 1.6.2 函数-Lambda 函数与表达式
/**
 * C++ 函数
 * 函数是一组一起执行一个任务的语句。每个 C++ 程序都至少有一个函数，即主函数 main() ，所有简单的程序都可以定义其他额外的函数。
 * 您可以把代码划分到不同的函数中。如何划分代码到不同的函数中是由您来决定的，但在逻辑上，划分通常是根据每个函数执行一个特定的任务来进行的。
 * 函数声明告诉编译器函数的名称、返回类型和参数。函数定义提供了函数的实际主体。 
 * C++ 标准库提供了大量的程序可以调用的内置函数。例如，函数 strcat() 用来连接两个字符串，函数 memcpy() 用来复制内存到另一个位置。
 * 函数还有很多叫法，比如方法、子例程或程序，等等。
 * 
 * ---------------------
 * Lambda 函数与表达式
 * 译 Lambda n. 希腊字母表的第11个字母 ；读 [ˈlæmdə] 兰姆达
 * 
 * C++11 提供了对匿名函数的支持,称为 Lambda 函数(也叫 Lambda 表达式)。
 * Lambda 表达式把函数看作对象。Lambda 表达式可以像对象一样使用，比如可以将它们赋给变量和作为参数（普通函数可以作为参数传递吗？当然可以）传递，还可以像函数一样对其求值。
 * 
 * Lambda 表达式本质上与函数声明非常类似。Lambda 表达式具体形式如下:
 * [capture](parameters)->return-type{body}
 * 译: capture vt.俘虏;俘获;捕获;用武力夺取;攻取;攻占;夺得;赢得;争得  n.(被)捕获;(被)俘获
 * 读: [ˈkæptʃə(r)] 
 * 例如：
 * [](int x, int y){ return x < y ; }
 * 
 * 如果没有返回值可以表示为：
 * [capture](parameters){body}
 * 例如：
 * []{ ++global_x; } 
 * 
 * 在一个更为复杂的例子中，返回类型可以被明确的指定如下：
 * [](int x, int y) -> int { int z = x + y; return z + x; }
 * 本例中，一个临时的参数 z 被创建用来存储中间结果。如同一般的函数，z 的值不会保留到下一次该不具名函数再次被调用时。
 * 
 * 如果 lambda 函数没有传回值（例如 void），其返回类型可被完全忽略。
 * 
 * 在Lambda表达式内可以访问当前作用域的变量，这是Lambda表达式的闭包（Closure）行为。 与JavaScript闭包不同，C++变量传递有传值和传引用的区别。可以通过前面的[]来指定：
 * 译: Closure 闭包;闭合;封闭性;关闭;结束
 * 读: [ˈkləʊʒə(r)]
    []      // 沒有定义任何变量。使用未定义变量会引发错误。
    [x, &y] // x以传值方式传入（默认），y以引用方式传入。
    [&]     // 任何被使用到的外部变量都隐式地以引用方式加以引用。
    [=]     // 任何被使用到的外部变量都隐式地以传值方式加以引用。
    [&, x]  // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
    [=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用。
 * 
 * 另外有一点需要注意。对于[=]或[&]的形式，lambda 表达式可以直接使用 this 指针。但是，对于[]的形式，如果要使用 this 指针，必须显式传入：
 * [this]() { this->someFunc(); }();
 * 
 * ~~~~~~~~~~~~~~~~~~~~~~~~
 * lambda函数 - capture
*/
#include <iostream>
void increase(int);
int main(void)
{
  // 先看个例子
  /**
   * [] 沒有定义任何变量。使用未定义变量会引发错误。
  */

  // std::cout << "---------[]-------------" << std::endl;
  // int num0 = 0;
  // auto counter0 = []
  // {
  //   num0++; // error: 'num0' is not captured
  // };

  // counter0();
  // std::cout << "num0 : " << num0 << std::endl;

  /**
   * [x, &y] // x以传值方式传入（默认），y以引用方式传入。
   * 普通函数的传值调用，对参数进行修改时并不会报错，而它会
  */
  std::cout << "---------[x, &y]-------------" << std::endl;

  int a1 = 0;
  int b1 = 0;
  auto counter1 = [a1, &b1]
  {
    // a1++; // error: increment of read-only variable 'a1'
    std::cout << "a1 : " << a1 << std::endl;
    b1++;
  };

  // increase(a1);

  counter1();
  std::cout << "a1 : " << a1 << "\n"
            << "b1 : " << b1 << std::endl;

  /**
   * [&] 任何被使用到的外部变量都隐式地以引用方式加以引用。
  */
  std::cout << "---------[&]-------------" << std::endl;

  int a2 = 0;
  int b2 = 0;
  auto counter2 = [&]
  {
    a2++;
    // std::cout << "a2 : " << a2 << std::endl;
    b2++;
  };

  counter2();
  std::cout << "a2 : " << a2 << "\n"
            << "b2 : " << b2 << std::endl;

  /**
   * [=] 任何被使用到的外部变量都隐式地以传值方式加以引用。
  */
  std::cout << "---------[=]-------------" << std::endl;

  int a3 = 0;
  int b3 = 0;
  auto counter3 = [=]
  {
    // a3++;
    // b3++;
    std::cout << "a3 : " << a3 << "\n"
              << "b3 : " << b3 << std::endl;
  };

  counter3();
  // std::cout << "a3 : " << a3 << "\n"
  //           << "b3 : " << b3 << std::endl;

  /**
   * [&, x] x显式地以传值方式加以引用。其余变量以引用方式加以引用。
  */
  std::cout << "---------[&, x]-------------" << std::endl;
  int a4 = 0;
  int b4 = 0;
  [&, b4]
  {
    a4++;
    // b4++;
    std::cout << "a4 : " << a4 << "\n"
              << "b4 : " << b4 << std::endl;
  }();

  /**
   * [=, &z] z显式地以引用方式加以引用。其余变量以传值方式加以引用
  */
  std::cout << "---------[=, &z]-------------" << std::endl;
  int a5 = 0;
  int b5 = 0;
  [=, &b5]
  {
    // a5++;
    b5++;
    std::cout << "a5 : " << a5 << "\n"
              << "b5 : " << b5 << std::endl;
  }();

  std::cout << "---------[capture] this指针 使用------------" << std::endl;

  // 对于[=]或[&]的形式，lambda 表达式可以直接使用 this 指针。
  std::string hhh = "哈哈哈";

  // 学对象的时候在说
  // [=]
  // {
  //   std::cout << this->hhh << std::endl;
  // };

  // 但是，对于[]的形式，如果要使用 this 指针，必须显式传入：
  // [this]
  // {
  //   std::cout << this->hhh << std::endl;
  // }();// 通过引用捕获当前对象 其实是复制指针

  // [*this]{} // 通过传值方式捕获当前对象

  /**
   * lambda函数总结2
   * 1. capture值可以为:[]、[&]、[=]、[x,&y]、[&,z]、[=,&z]
   * 2. [=]、[&] 可以直接使用this, []不行 必须显式传入 [this]
   * 
  */

  printf("---------end-----------");
  return 0;
}

void increase(int a)
{
  a++;
  std::cout << "普通函数传值调用" << std::endl;
}
